#include "task.h"
#include "taskqueue.h"
#include <cassert>
#include <errno.h>
#include "autodone.h"
#include "log/logger.h"
#include <stdexcept>

namespace thread{



TaskQueue::TaskQueue(
    uint32_t queue_threshold,
    uint32_t queue_max_limit):
    queue_threshold_(queue_threshold),
    queue_max_limit_(queue_max_limit)
{
  MEMBER_DEBUG_LOG("[+/-]TaskQueue::TaskQueue()");
}

TaskQueue::~TaskQueue()
{
  MEMBER_DEBUG_LOG("[+]TaskQueue::~TaskQueue");
  Task* tk = NULL;
  AutoDone< SimpleLock > guard( lock_, &SimpleLock::Lock, &SimpleLock::Unlock );
  while ( !tasks_.empty() )
  {
    tk = tasks_.front();
    tasks_.pop();
    tk->Release(); // release resource
  }
  MEMBER_DEBUG_LOG("[-]TaskQueue::~TaskQueue");
}

Task* TaskQueue::Pop()
{
  MEMBER_DEBUG_LOG("[+]TaskQueue::Pop");
  semaphore_.Wait();
  Task* tk = NULL;
  AutoDone< SimpleLock > guard( lock_, &SimpleLock::Lock, &SimpleLock::Unlock );
  if ( !tasks_.empty() )
  {
    tk = tasks_.front();
    tasks_.pop();
  }
  MEMBER_DEBUG_LOG("[-]TaskQueue::Pop %p", tk);
  return tk;
}

bool TaskQueue::Push(Task* tk)
{
  MEMBER_DEBUG_LOG("[+]TaskQueue::Push %p", tk);
  assert(tk);
  bool ret = true;
  try
  {
    AutoDone< SimpleLock > guard( lock_, &SimpleLock::Lock, &SimpleLock::Unlock );
    tasks_.push( tk );
    size_t queue_size = tasks_.size();
    if ( queue_size > queue_max_limit_)
    {
      MEMBER_LOG(LOG_ERR, "task queue reach max limit, drop one oldest task");
      tk = tasks_.front();
      tasks_.pop();    // drop the old task
      tk->Release();
    }
    else
    {
      if ( queue_size > queue_threshold_ )
      {
        MEMBER_LOG(LOG_WARNING, "task queue size(%Zu) above threshold", queue_size);
      }
      semaphore_.Post();
    }
  }
  catch ( std::exception& e)
  {
    MEMBER_LOG( LOG_ERR, "Failed to queue task: %s", e.what() );
    ret = false;
  }
  catch(...)
  {
    MEMBER_LOG(LOG_ERR, "unknown exception");
    ret = false;
  }
  MEMBER_DEBUG_LOG( "[-]TaskQueue::Push() => returned %s",
    ret ? "true" : "false" );
  return ret;
}

uint32_t TaskQueue::Size()
{
    AutoDone< SimpleLock > guard( lock_, &SimpleLock::Lock, &SimpleLock::Unlock );
    return tasks_.size();
}

}

